/*
 * Copyright (c) 2013 Google, Inc
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <asm/arch/hardware.h>

#define OFFSET_GPIO_SWPORTA_DR         (0x0000)
#define OFFSET_GPIO_SWPORTA_DDR        (0x0004)
#define OFFSET_GPIO_PORTA_CTL          (0x0008)
#define OFFSET_GPIO_SWPORTB_DR         (0x000C)
#define OFFSET_GPIO_SWPORTB_DDR        (0x0010)
#define OFFSET_GPIO_PORTB_CTL          (0x0014)
#define OFFSET_GPIO_INTEN              (0x0030)
#define OFFSET_GPIO_INTMASK            (0x0034)
#define OFFSET_GPIO_INTTYPE_LEVEL      (0x0038)
#define OFFSET_GPIO_INT_POLARITY       (0x003C)
#define OFFSET_GPIO_INTSTATUS          (0x0040)
#define OFFSET_GPIO_RAWINTSTATUS       (0x0044)
#define OFFSET_GPIO_DEBOUNCE           (0x0048)
#define OFFSET_GPIO_PORTA_EOI          (0x004C)
#define OFFSET_GPIO_EXT_PORTA          (0x0050)
#define OFFSET_GPIO_EXT_PORTB          (0x0054)

static struct array_bank{
		uint base;
		uint dr_offset;
		uint ddr_offset;
		uint ext_offset ;
} Bank[] = {
		{
				.base = GPIO0_REG_BASE,
				.dr_offset = OFFSET_GPIO_SWPORTA_DR,
				.ddr_offset = OFFSET_GPIO_SWPORTA_DDR,
				.ext_offset = OFFSET_GPIO_EXT_PORTA
		},
		{
				.base = GPIO1_REG_BASE,
				.dr_offset = OFFSET_GPIO_SWPORTA_DR,
				.ddr_offset = OFFSET_GPIO_SWPORTA_DDR,
				.ext_offset = OFFSET_GPIO_EXT_PORTA
		},
#ifdef GPIO2_REG_BASE
		{
				.base = GPIO2_REG_BASE,
				.dr_offset = OFFSET_GPIO_SWPORTA_DR,
				.ddr_offset = OFFSET_GPIO_SWPORTA_DDR,
				.ext_offset = OFFSET_GPIO_EXT_PORTA
		},
#endif
#ifdef GPIO3_REG_BASE
		{
				.base = GPIO3_REG_BASE,
				.dr_offset = OFFSET_GPIO_SWPORTA_DR,
				.ddr_offset = OFFSET_GPIO_SWPORTA_DDR,
				.ext_offset = OFFSET_GPIO_EXT_PORTA
		},
#endif
#ifdef GPIO4_REG_BASE
		{
				.base = GPIO4_REG_BASE,
				.dr_offset = OFFSET_GPIO_SWPORTA_DR,
				.ddr_offset = OFFSET_GPIO_SWPORTA_DDR,
				.ext_offset = OFFSET_GPIO_EXT_PORTA
		},
#endif
};

int gpio_request(unsigned gpio, const char *label)
{
	return 0;
}

int gpio_free(unsigned gpio)
{
	return 0;
}

int gpio_direction_input(unsigned gpio)
{
	int bank, no;
	int dir;

	if (gpio >= 0 && gpio < CONFIG_GPIO_NUMBER)
	{
		bank = gpio / 32 ;
		no   = gpio % 32;
		dir  = GET_REG(Bank[bank].base + Bank[bank].ddr_offset);
		dir &= ~(0x1 << no);
		SET_REG(Bank[bank].base + Bank[bank].ddr_offset, dir);
		return 0;
	}

	return -1;
}

int gpio_direction_output(unsigned gpio, int value)
{
	int bank, no;
	int dir, val;

	if (gpio >= 0 && gpio < CONFIG_GPIO_NUMBER)
	{
		bank = gpio / 32 ;
		no   = gpio % 32;

		val = GET_REG(Bank[bank].base + Bank[bank].dr_offset);
		if (value == 1)
			val |= (0x1 << no);
		else if (value == 0)
			val &= ~(0x1 << no);
		SET_REG(Bank[bank].base + Bank[bank].dr_offset, val);

		dir  = GET_REG(Bank[bank].base + Bank[bank].ddr_offset);
		dir |= (0x1 << no);
		SET_REG(Bank[bank].base + Bank[bank].ddr_offset, dir);

		return 0;
	}

	return -1;
}

int gpio_get_value(unsigned gpio)
{
	int bank, no;
	int value;

	if (gpio >= 0 && gpio < CONFIG_GPIO_NUMBER)
	{
		bank  = gpio / 32 ;
		no    = gpio % 32;
		value = GET_REG(Bank[bank].base + Bank[bank].ext_offset) & (0x1 << no);
		return value ? 1 : 0;
	}

	return -1;
}

int gpio_set_value(unsigned gpio, int value)
{
	return gpio_direction_output(gpio, value);
}

